home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Information / CSMP Digest / volume 1 / csmp-v1-225.txt < prev    next >
Text File  |  1992-12-31  |  74KB  |  1,966 lines

  1. C.S.M.P. Digest             Mon, 21 Dec 92       Volume 1 : Issue 225
  2.  
  3. Today's Topics:
  4.  
  5.     Help! making an assembly routine faster
  6.  
  7.  
  8.  
  9. The Comp.Sys.Mac.Programmer Digest is moderated by Michael A. Kelly.
  10.  
  11. The digest is a collection of article threads from the internet newsgroup
  12. comp.sys.mac.programmer.  It is designed for people who read c.s.m.p. semi-
  13. regularly and want an archive of the discussions.  If you don't know what a
  14. newsgroup is, you probably don't have access to it.  Ask your systems
  15. administrator(s) for details.  You can post articles to any newsgroup by
  16. mailing your article to newsgroup@ucbvax.berkeley.edu.  So, to post an
  17. article to comp.sys.mac.programmer, you mail it to
  18. comp-sys-mac-programmer@ucbvax.berkeley.edu.  Note the '-' instead of '.'
  19. in the newsgroup name.
  20.  
  21. Each issue of the digest contains one or more sets of articles (called
  22. threads), with each set corresponding to a 'discussion' of a particular
  23. subject.  The articles are not edited; all articles included in this digest
  24. are in their original posted form (as received by our news server at
  25. cs.uoregon.edu).  Article threads are not added to the digest until the last
  26. article added to the thread is at least one month old (this is to ensure that
  27. the thread is dead before adding it to the digest).  Article threads that
  28. consist of only one message are generally not included in the digest.
  29.  
  30. The entire digest is available for anonymous ftp from ftp.cs.uoregon.edu
  31. [128.223.8.8] in the directory /pub/mac/csmp-digest.  Be sure to read the
  32. file /pub/mac/csmp-digest/README before downloading any files.  The most
  33. recent issues are available from sumex-aim.stanford.edu [36.44.0.6] in the
  34. directory /info-mac/digest/csmp.  If you don't have ftp capability, the sumex
  35. archive has a mail server; send a message with the text '$MACarch help' (no
  36. quotes) to LISTSERV@ricevm1.rice.edu for more information.
  37.  
  38. The digest is also available via email.  Just send a note saying that you
  39. want to be on the digest mailing list to mkelly@cs.uoregon.edu, and you will
  40. automatically receive each new issue as it is created.  Sorry, back issues
  41. are not available through the mailing list.
  42.  
  43. Send administrative mail to mkelly@cs.uoregon.edu.
  44.  
  45.  
  46. -------------------------------------------------------
  47.  
  48. From: mkelly@mystix.cs.uoregon.edu (Michael A. Kelly)
  49. Subject: Help! making an assembly routine faster
  50. Organization: High Risk Ventures
  51. Date: Sat, 14 Nov 1992 09:19:05 GMT
  52.  
  53.  
  54. Hey, all you assembly hackers!  How can I make this routine faster?  As it
  55. is it's only about 40% faster than CopyMask.  (I'm using Think C 5.)
  56.  
  57.  
  58. /*
  59.  * Quick8CopyMask
  60.  *
  61.  *  The QuickXCopyMask family are much faster versions of CopyMask
  62.  *  that don't do clipping, dithering, etc.  The source and destination
  63.  *  PixMaps are expected to have the same bit depth.  The X in the name
  64.  *  represents the expected bit depth of the source and destination PixMaps.
  65.  *
  66.  *  The mask is excpected to be exactly the same size as the rectangle
  67.  *  that is being copied.
  68.  *
  69.  */
  70.  
  71. void Quick8CopyMask(
  72.     PixMapHandle    srcMap,
  73.     PixMapHandle    dstMap,
  74.     Ptr             mask,
  75.     Point           srcPt,
  76.     Point           dstPt,
  77.     short           width,
  78.     short           height )
  79. {
  80.  
  81.     register char   *src;
  82.     register char   *dst;
  83.     register long   srcNewline;
  84.     register long   dstNewline;
  85.     char            mode32 = QD32COMPATIBLE;
  86.     short           w = (width >> 2) - 1;
  87.     short           e = width % 4 - 1;
  88.     short           h = height - 1;
  89.     
  90.     // Set up pointers to the beginning of the memory to copy
  91.     // and calculate the newline value for the source and destination
  92.     
  93.     src = GetPixBaseAddr( srcMap ) + (long) ((*srcMap)->rowBytes & 0x3FFF) * srcPt.v + srcPt.h;
  94.     srcNewline = ((*srcMap)->rowBytes & 0x3FFF) - width;
  95.     
  96.     dst = GetPixBaseAddr( dstMap ) + (long) ((*dstMap)->rowBytes & 0x3FFF) * dstPt.v + dstPt.h;
  97.     dstNewline = ((*dstMap)->rowBytes & 0x3FFF) - width;
  98.  
  99.     // Switch into 32 bit addressing mode
  100.     
  101.     SwapMMUMode( &mode32 );
  102.     
  103.     // Copy the rect from the source to the destination
  104.     
  105.     asm {
  106.     
  107.         MOVE.W    h, D0               ; put height loop variable in D0
  108.         MOVEA.L   src, A0             ; put the source pixmap address in A0
  109.         MOVEA.L   dst, A1             ; put the destination address in A1
  110.         MOVEA.L   mask, A2            ; put the mask address in A2
  111.         MOVE.L    #0, D3
  112.     
  113.     @1:                               ; copy the next row
  114.         MOVE.W    w, D1
  115.         
  116.     @2:                               ; copy the next four bytes in the row
  117.     
  118.         MOVEQ     #0, D2              ; test the next four bits in the mask
  119.         BFTST     (A2){D3:1}          ; test the bit
  120.         BEQ       @bit2               ; if zero, go to bit 2
  121.         ORI.L     #0xFF000000, D2     ; else add to pixel mask
  122.     @bit2:
  123.         ADDQ.L    #1, D3              ; increment the bit number
  124.         BFTST     (A2){D3:1}          ; test the bit
  125.         BEQ       @bit3               ; if zero, go to bit 3
  126.         ORI.L     #0x00FF0000, D2     ; else add to pixel mask
  127.     @bit3:
  128.         ADDQ.L    #1, D3              ; increment the bit number
  129.         BFTST     (A2){D3:1}          ; test the bit
  130.         BEQ       @bit4               ; if zero, go to bit 4
  131.         ORI.L     #0x0000FF00, D2     ; else add to pixel mask
  132.     @bit4:
  133.         ADDQ.L    #1, D3              ; increment the bit number
  134.         BFTST     (A2){D3:1}          ; test the bit
  135.         BEQ       @inc                ; if zero, continue
  136.         ORI.L     #0x000000FF, D2     ; else add to pixel mask
  137.     @inc:
  138.         ADDQ.L    #1, D3              ; increment the bit number
  139.  
  140.  
  141. ; speeding this next part up would make a big difference, but how?
  142.        
  143.         MOVE.L    D2, D4              ; save the mask
  144.         NOT.L     D4                  ; invert the mask
  145.         AND.L     (A0)+, D2           ; compute the pixels to be copied
  146.         AND.L     (A1), D4            ; compute the pixels to be saved
  147.         OR.L      D2, D4              ; combine the copied and saved pixels
  148.         MOVE.L    D4, (A1)+           ; copy the pixels
  149.         
  150.         DBF       D1, @2
  151.         
  152.         TST.W     e
  153.         BLT       @4                  ; continue if e is less than 0
  154.     
  155.         MOVE.W    e, D1               ; copy the extra bytes, if any
  156.     
  157.     @3:                               ; copy the next byte
  158.     
  159.         BFTST     (A2){D3:1}          ; test the next bit in the mask
  160.         BEQ       @incb               ; if zero, continue
  161.         MOVE.B    (A0)+, (A1)+        ; else copy the pixel
  162.     @incb:
  163.         ADDQ.L    #1, D3              ; increment the bit number
  164.         
  165.         DBF       D1, @3
  166.     
  167.     @4:
  168.         ADDA.L    srcNewline, A0      ; bring the src pointer to the start of the next row
  169.         ADDA.L    dstNewline, A1      ; bring the dst pointer to the start of the next row
  170.         
  171.         DBF       D0, @1
  172.     
  173.     }
  174.     
  175.     // Switch back to the previous addressing mode
  176.     
  177.     SwapMMUMode( &mode32 );
  178.  
  179. }
  180.  
  181.  
  182.  
  183.  
  184. I'm new to assembly, as you can probably tell.  I'm open to all suggestions.
  185.  
  186. Thanks,
  187.  
  188. Mike.
  189. - -- 
  190. _____________________________________________________________________________
  191. Michael A. Kelly                                               Senior Partner
  192. mkelly@cs.uoregon.edu                                      High Risk Ventures
  193. _____________________________________________________________________________
  194.  
  195. +++++++++++++++++++++++++++
  196.  
  197. From: jmunkki@vipunen.hut.fi (Juri Munkki)
  198. Date: 14 Nov 92 20:08:31 GMT
  199. Organization: Helsinki University of Technology
  200.  
  201. In article <1992Nov14.091905.29520@cs.uoregon.edu> mkelly@mystix.cs.uoregon.edu (Michael A. Kelly) writes:
  202. >Hey, all you assembly hackers!  How can I make this routine faster?  As it
  203. >is it's only about 40% faster than CopyMask.  (I'm using Think C 5.)
  204.  
  205. This sounds like something I might be able  to help with... let's see...
  206.  
  207. >    src = GetPixBaseAddr( srcMap ) + (long) ((*srcMap)->rowBytes & 0x3FFF) * srcPt.v + srcPt.h;
  208.  
  209. Shouldn't you cast to long before the multiply? It looks to me like you are
  210. casting the result of a short multiply, but I could be wrong, since I don't
  211. want to check this from a C book right now.
  212.  
  213. >        MOVE.W    h, D0               ; put height loop variable in D0
  214. >        MOVEA.L   src, A0             ; put the source pixmap address in A0
  215. >        MOVEA.L   dst, A1             ; put the destination address in A1
  216. >        MOVEA.L   mask, A2            ; put the mask address in A2
  217. >        MOVE.L    #0, D3
  218. >    
  219. >    @1:                               ; copy the next row
  220. >        MOVE.W    w, D1
  221. >        
  222. >    @2:                               ; copy the next four bytes in the row
  223. >    
  224. >        MOVEQ     #0, D2              ; test the next four bits in the mask
  225. >        BFTST     (A2){D3:1}          ; test the bit
  226. >        BEQ       @bit2               ; if zero, go to bit 2
  227. >        ORI.L     #0xFF000000, D2     ; else add to pixel mask
  228. >    @bit2:
  229. >        ADDQ.L    #1, D3              ; increment the bit number
  230. >        BFTST     (A2){D3:1}          ; test the bit
  231. >        BEQ       @bit3               ; if zero, go to bit 3
  232. >        ORI.L     #0x00FF0000, D2     ; else add to pixel mask
  233. >    @bit3:
  234. >        ADDQ.L    #1, D3              ; increment the bit number
  235. >        BFTST     (A2){D3:1}          ; test the bit
  236. >        BEQ       @bit4               ; if zero, go to bit 4
  237. >        ORI.L     #0x0000FF00, D2     ; else add to pixel mask
  238. >    @bit4:
  239. >        ADDQ.L    #1, D3              ; increment the bit number
  240. >        BFTST     (A2){D3:1}          ; test the bit
  241. >        BEQ       @inc                ; if zero, continue
  242. >        ORI.L     #0x000000FF, D2     ; else add to pixel mask
  243. >    @inc:
  244. >        ADDQ.L    #1, D3              ; increment the bit number
  245.  
  246. Instead of the above code, extract as many bits as you want (I suggest
  247. 8 bits, but 4 is also ok) and then use this number as an index to a
  248. table of precalculated masks.
  249.  
  250. 8 bits is fast, because you don't need to use bitfield instructions to
  251. retrieve the value. You then grab two masks from bitfield tables, so
  252. you avoid all that ORI.L stuff and the increments to the bit numbers.
  253.  
  254. >; speeding this next part up would make a big difference, but how?
  255.  
  256. You could take care of longword alignment on all reads and writes to
  257. the destination buffer. This requires quite a bit of extra code, but it
  258. might be worth it, since memory accesses can become twice as fast for
  259. the video memory, which is usually very slow. Of course now that the
  260. processors have data caches (unlike the 68020), it's probably not all
  261. that critical.
  262.  
  263. Another possibility is to grab just a few mask bits (like 4, as I
  264. suggested) at a time and write special code for all the 16 possible
  265. cases. Use a jump table to select the code to use. In the usual case,
  266. where the mask is all black, you get a simple Move.l (An)+,(An)+, which
  267. really should do wonders to this routine. You also have 3 cases where
  268. you do a Move.w (An)+,(An)+ with some adjustments to the registers, 4
  269. cases of move.b, one case where you don't do anything, so that only
  270. leaves you with 7 more complicated cases, where you might want to use a
  271. constant mask.
  272.  
  273. I think you can get fairly good performance if you carefully code the
  274. two cases where you have an empty mask or a full mask. The rest occur
  275. less often.
  276.  
  277. You routine wastes most of its time in the mask handling code. You could
  278. have tested for this by doing timing tests where you replace some part
  279. of the code with fast dummy code and compare the relative speeds. Those
  280. parts that execute much faster as the dummy version need more attention
  281. than those where the difference is small.
  282.  
  283. - -- 
  284.   Juri Munkki                           Windsurf: fast sailing
  285.  jmunkki@hut.fi                          Macintosh: fast software
  286.  
  287. +++++++++++++++++++++++++++
  288.  
  289. From: mkelly@mystix.cs.uoregon.edu (Michael A. Kelly)
  290. Organization: University of Oregon Computer and Information Sciences Dept.
  291. Date: Mon, 16 Nov 1992 01:48:50 GMT
  292.  
  293. In article <1992Nov14.200831.20477@nntp.hut.fi> jmunkki@vipunen.hut.fi (Juri Munkki) writes:
  294. >In article <1992Nov14.091905.29520@cs.uoregon.edu> mkelly@mystix.cs.uoregon.edu (Michael A. Kelly) writes:
  295. >>Hey, all you assembly hackers!  How can I make this routine faster?  As it
  296. >>is it's only about 40% faster than CopyMask.  (I'm using Think C 5.)
  297. >
  298. >This sounds like something I might be able  to help with... let's see...
  299. >
  300. >>    src = GetPixBaseAddr( srcMap ) + (long) ((*srcMap)->rowBytes & 0x3FFF) * srcPt.v + srcPt.h;
  301. >
  302. >Shouldn't you cast to long before the multiply? It looks to me like you are
  303. >casting the result of a short multiply, but I could be wrong, since I don't
  304. >want to check this from a C book right now.
  305.  
  306. Yep, but if you look closely at the parens, I think you'll find that that's
  307. what I'm doing.
  308.  
  309. >Another possibility is to grab just a few mask bits (like 4, as I
  310. >suggested) at a time and write special code for all the 16 possible
  311. >cases. Use a jump table to select the code to use.
  312.  
  313. OK, I did that, and managed to almost triple the speed of my original routine,
  314. making the new routine about four times as fast as CopyMask.  And yet, I'd
  315. like to make it even faster.  So suggestions are welcome.
  316.  
  317. Someone else suggested that I just make the mask the same depth as the pixmaps,
  318. so that I could use the mask directly instead of having to extract bits from
  319. it.  This turned out to be slower than the jump table approach, only about
  320. three times as fast as CopyMask.  Of course, the problem could be with my
  321. assembly skills rather than with the theory.
  322.  
  323. So, here are the resulting routines.  The first uses the jump table approach,
  324. the second uses the wide mask approach.  Can they be made even faster??
  325.  
  326.  
  327. /*
  328.  * Quick8CopyMask
  329.  *
  330.  *    The QuickXCopyMask family are much faster versions of CopyMask
  331.  *    that don't do clipping, dithering, etc.  The source and destination
  332.  *    PixMaps are expected to have the same bit depth.  The X in the name
  333.  *    represents the expected bit depth of the source and destination PixMaps.
  334.  *
  335.  *    The mask is expected to be exactly the same size as the rectangle
  336.  *    that is being copied.
  337.  *
  338.  */
  339.  
  340. void Quick8CopyMask(
  341.     PixMapHandle    srcMap,
  342.     PixMapHandle    dstMap,
  343.     Ptr             mask,
  344.     Point           srcPt,
  345.     Point           dstPt,
  346.     short           width,
  347.     short           height )
  348. {
  349.  
  350.     register char   *src;
  351.     register char   *dst;
  352.     register long   srcNewline;
  353.     register long   dstNewline;
  354.     char            mode32 = QD32COMPATIBLE;
  355.     short           w = (width >> 3) - 1;
  356.     short           e = (width & 0x07) - 1;
  357.     short           h = height - 1;
  358.     
  359.     // Set up pointers to the beginning of the memory to copy
  360.     // and calculate the newline value for the source and destination
  361.     
  362.     src = GetPixBaseAddr( srcMap ) + (long) ((*srcMap)->rowBytes & 0x3fff) * srcPt.v + srcPt.h;
  363.     srcNewline = ((*srcMap)->rowBytes & 0x3fff) - width;
  364.     
  365.     dst = GetPixBaseAddr( dstMap ) + (long) ((*dstMap)->rowBytes & 0x3fff) * dstPt.v + dstPt.h;
  366.     dstNewline = ((*dstMap)->rowBytes & 0x3fff) - width;
  367.  
  368.     // Switch into 32 bit addressing mode
  369.     
  370.     SwapMMUMode( &mode32 );
  371.     
  372.     // Copy the rect from the source to the destination
  373.     
  374.     asm {
  375.     
  376.         MOVE.W    h, D0               ; put height loop variable in D0
  377.         MOVEA.L   src, A0             ; put the source pixmap address in A0
  378.         MOVEA.L   dst, A1             ; put the destination address in A1
  379.         MOVEA.L   mask, A2            ; put the mask address in A2
  380.         
  381.     @1:                               ; copy the next row
  382.         MOVE.W    w, D1
  383.         
  384.     @2:                               ; copy the next eight bytes in the row
  385.     
  386.         MOVE.B    (A2), D2            ; copy the next mask byte
  387.         
  388.         TST.B     D2
  389.         BEQ       @nocopy             ; if zero, don't copy anything
  390.         
  391.         CMPI.B    #0xFF, D2
  392.         BNE       @hardway            ; don't copy everything
  393.         
  394.         MOVE.L    (A0)+, (A1)+        ; copy all bytes
  395.         MOVE.L    (A0)+, (A1)+
  396.         ADDQ.L    #1, A2
  397.         JMP       @endloop
  398.     
  399.     @nocopy:                          ; copy no bytes
  400.         ADDQ.L    #8, A0
  401.         ADDQ.L    #8, A1
  402.         ADDQ.L    #1, A2
  403.         JMP       @endloop
  404.     
  405.     @hardway:
  406.         ANDI.L    #0xF0, D2           ; mask off the low four bits
  407.         LSR.W     #4, D2              ; shift bits 4-7 into bits 0-3
  408.         ADD.W     D2, D2              ; double the index
  409.         ADD.W     @table(D2.W), D2    ; calculate the address
  410.         JSR       @table(D2.W)        ; plot four pixels
  411.         
  412.         CLR.L     D2                  ; clear the mask register
  413.         MOVE.B    (A2)+, D2           ; copy the next mask byte
  414.         ANDI.B    #0xF, D2            ; mask off the high four bits
  415.         ADD.W     D2, D2              ; double the index
  416.         ADD.W     @table(D2.W), D2    ; calculate the address
  417.         JSR       @table(D2.W)        ; plot four pixels
  418.     
  419.     @endloop:
  420.         DBF       D1, @2
  421.         
  422.         TST.W     e
  423.         BLT       @4                  ; continue if e is less than 0
  424.     
  425.         MOVE.W    e, D1               ; copy the extra bytes, if any
  426.     
  427.     @3:                               ; copy the next byte
  428.  
  429.         MOVEQ.L   #0, D3              ; initialize the bit counter    
  430.         BTST      D3, (A2)            ; test the next bit in the mask
  431.         BEQ       @skip               ; if zero, continue
  432.         MOVE.B    (A0)+, (A1)+        ; else copy the pixel
  433.         JMP       @incb
  434.     @skip:
  435.         ADDQ.L    #1, A0
  436.         ADDQ.L    #1, A1
  437.     @incb:
  438.         ADDQ.L    #1, D3              ; increment the bit number
  439.         
  440.         DBF       D1, @3
  441.     
  442.     @4:
  443.         ADDA.L    srcNewline, A0      ; bring the src pointer to the start of the next row
  444.         ADDA.L    dstNewline, A1      ; bring the dst pointer to the start of the next row
  445.         
  446.         DBF       D0, @1
  447.         
  448.         JMP       @end                ; skip to the end
  449.         
  450.     @table:
  451.         DC.W      @sub0
  452.         DC.W      @sub1
  453.         DC.W      @sub2
  454.         DC.W      @sub3
  455.         DC.W      @sub4
  456.         DC.W      @sub5
  457.         DC.W      @sub6
  458.         DC.W      @sub7
  459.         DC.W      @sub8
  460.         DC.W      @sub9
  461.         DC.W      @sub10
  462.         DC.W      @sub11
  463.         DC.W      @sub12
  464.         DC.W      @sub13
  465.         DC.W      @sub14
  466.         DC.W      @sub15
  467.     
  468.     @sub0:                            ; mask = 0000, draw nothing
  469.         ADDQ.L    #4, A0
  470.         ADDQ.L    #4, A1
  471.         RTS
  472.     
  473.     @sub1:                            ; mask = 0001
  474.         ADDQ.L    #3, A0
  475.         ADDQ.L    #3, A1
  476.         MOVE.B    (A0)+, (A1)+
  477.         RTS
  478.     
  479.     @sub2:                            ; mask = 0010
  480.         ADDQ.L    #2, A0
  481.         ADDQ.L    #2, A1
  482.         MOVE.B    (A0)+, (A1)+
  483.         ADDQ.L    #1, A0
  484.         ADDQ.L    #1, A1
  485.         RTS
  486.     
  487.     @sub3:                            ; mask = 0011
  488.         ADDQ.L    #2, A0
  489.         ADDQ.L    #2, A1
  490.         MOVE.W    (A0)+, (A1)+
  491.         RTS
  492.     
  493.     @sub4:                            ; mask = 0100
  494.         ADDQ.L    #1, A0
  495.         ADDQ.L    #1, A1
  496.         MOVE.B    (A0)+, (A1)+
  497.         ADDQ.L    #2, A0
  498.         ADDQ.L    #2, A1
  499.         RTS
  500.     
  501.     @sub5:                            ; mask = 0101
  502.         ADDQ.L    #1, A0
  503.         ADDQ.L    #1, A1
  504.         MOVE.B    (A0)+, (A1)+
  505.         ADDQ.L    #1, A0
  506.         ADDQ.L    #1, A1
  507.         MOVE.B    (A0)+, (A1)+
  508.         RTS
  509.     
  510.     @sub6:                            ; mask = 0110
  511.         ADDQ.L    #1, A0
  512.         ADDQ.L    #1, A1
  513.         MOVE.B    (A0)+, (A1)+
  514.         ADDQ.L    #1, A0
  515.         ADDQ.L    #1, A1
  516.         RTS
  517.     
  518.     @sub7:                            ; mask = 0111
  519.         ADDQ.L    #1, A0
  520.         ADDQ.L    #1, A1
  521.         MOVE.B    (A0)+, (A1)+
  522.         MOVE.W    (A0)+, (A1)+
  523.         RTS
  524.     
  525.     @sub8:                            ; mask = 1000
  526.         MOVE.B    (A0)+, (A1)+
  527.         ADDQ.L    #3, A0
  528.         ADDQ.L    #3, A1
  529.         RTS
  530.     
  531.     @sub9:                            ; mask = 1001
  532.         MOVE.B    (A0)+, (A1)+
  533.         ADDQ.L    #2, A0
  534.         ADDQ.L    #2, A1
  535.         MOVE.B    (A0)+, (A1)+
  536.         RTS
  537.     
  538.     @sub10:                            ; mask = 1010
  539.         MOVE.B    (A0)+, (A1)+
  540.         ADDQ.L    #1, A0
  541.         ADDQ.L    #1, A1
  542.         MOVE.B    (A0)+, (A1)+
  543.         ADDQ.L    #1, A0
  544.         ADDQ.L    #1, A1
  545.         RTS
  546.     
  547.     @sub11:                            ; mask = 1011
  548.         MOVE.B    (A0)+, (A1)+
  549.         ADDQ.L    #1, A0
  550.         ADDQ.L    #1, A1
  551.         MOVE.W    (A0)+, (A1)+
  552.         RTS
  553.     
  554.     @sub12:                            ; mask = 1100
  555.         MOVE.W    (A0)+, (A1)+
  556.         ADDQ.L    #2, A0
  557.         ADDQ.L    #2, A1
  558.         RTS
  559.     
  560.     @sub13:                            ; mask = 1101
  561.         MOVE.W    (A0)+, (A1)+
  562.         ADDQ.L    #1, A0
  563.         ADDQ.L    #1, A1
  564.         MOVE.B    (A0)+, (A1)+
  565.         RTS
  566.     
  567.     @sub14:                            ; mask = 1110
  568.         MOVE.W    (A0)+, (A1)+
  569.         MOVE.B    (A0)+, (A1)+
  570.         ADDQ.L    #1, A0
  571.         ADDQ.L    #1, A1
  572.         RTS
  573.     
  574.     @sub15:                            ; mask = 1111
  575.         MOVE.L    (A0)+, (A1)+
  576.         RTS
  577.     
  578.     @end:
  579.     
  580.     }
  581.     
  582.     // Switch back to the previous addressing mode
  583.     
  584.     SwapMMUMode( &mode32 );
  585.  
  586. }
  587.  
  588.  
  589.  
  590. And the wide mask approach:
  591.  
  592.  
  593. void Quick8CopyMask(
  594.     PixMapHandle    srcMap,
  595.     PixMapHandle    dstMap,
  596.     Ptr             mask,
  597.     Point           srcPt,
  598.     Point           dstPt,
  599.     short           width,
  600.     short           height )
  601. {
  602.  
  603.     register char   *src;
  604.     register char   *dst;
  605.     register long   srcNewline;
  606.     register long   dstNewline;
  607.     char            mode32 = QD32COMPATIBLE;
  608.     short           w = (width >> 2) - 1;
  609.     short           e = (width & 0x3) - 1;
  610.     short           h = height - 1;
  611.     
  612.     // Set up pointers to the beginning of the memory to copy
  613.     // and calculate the newline value for the source and destination
  614.     
  615.     src = GetPixBaseAddr( srcMap ) + (long) ((*srcMap)->rowBytes & 0x3fff) * srcPt.v + srcPt.h;
  616.     srcNewline = ((*srcMap)->rowBytes & 0x3fff) - width;
  617.     
  618.     dst = GetPixBaseAddr( dstMap ) + (long) ((*dstMap)->rowBytes & 0x3fff) * dstPt.v + dstPt.h;
  619.     dstNewline = ((*dstMap)->rowBytes & 0x3fff) - width;
  620.  
  621.     // Switch into 32 bit addressing mode
  622.     
  623.     SwapMMUMode( &mode32 );
  624.     
  625.     // Copy the rect from the source to the destination
  626.     
  627.     asm {
  628.     
  629.         MOVE.W    h, D0               ; put height loop variable in D0
  630.         MOVEA.L   src, A0             ; put the source pixmap address in A0
  631.         MOVEA.L   dst, A1             ; put the destination address in A1
  632.         MOVEA.L   mask, A2            ; put the mask address in A2
  633.         
  634.     @1:                               ; copy the next row
  635.         MOVE.W    w, D1
  636.         
  637.     @2:                               ; copy the next four bytes in the row
  638.         
  639.         MOVE.L    (A2)+, D2           ; copy the mask to D2
  640.         MOVE.L    D2, D4              ; save the mask
  641.         NOT.L     D4                  ; invert the mask
  642.         AND.L     (A0)+, D2           ; compute the pixels to be copied
  643.         AND.L     (A1), D4            ; compute the pixels to be saved
  644.         OR.L      D2, D4              ; combine the copied and saved pixels
  645.         MOVE.L    D4, (A1)+           ; copy the pixels
  646.         
  647.         DBF       D1, @2
  648.         
  649.         TST.W     e
  650.         BLT       @4                  ; continue if e is less than 0
  651.     
  652.         MOVE.W    e, D1               ; copy the extra bytes, if any
  653.     
  654.     @3:                               ; copy the next byte
  655.     
  656.         MOVE.B    (A2)+, D2           ; copy the mask to D2
  657.         MOVE.B    D2, D4              ; save the mask
  658.         NOT.B     D4                  ; invert the mask
  659.         AND.B     (A0)+, D2           ; compute the pixels to be copied
  660.         AND.B     (A1), D4            ; compute the pixels to be saved
  661.         OR.B      D2, D4              ; combine the copied and saved pixels
  662.         MOVE.B    D4, (A1)+           ; copy the pixels
  663.         
  664.         DBF       D1, @3
  665.     
  666.     @4:
  667.         ADDA.L    srcNewline, A0      ; bring the src pointer to the start of the next row
  668.         ADDA.L    dstNewline, A1      ; bring the dst pointer to the start of the next row
  669.         
  670.         DBF       D0, @1
  671.     
  672.     }
  673.     
  674.     // Switch back to the previous addressing mode
  675.     
  676.     SwapMMUMode( &mode32 );
  677.  
  678. }
  679.  
  680.  
  681.  
  682. - -- 
  683. _____________________________________________________________________________
  684. Michael A. Kelly                                               Senior Partner
  685. mkelly@cs.uoregon.edu                                      High Risk Ventures
  686. _____________________________________________________________________________
  687.  
  688. +++++++++++++++++++++++++++
  689.  
  690. From: jmunkki@vipunen.hut.fi (Juri Munkki)
  691. Date: 16 Nov 92 19:09:47 GMT
  692. Organization: Helsinki University of Technology
  693.  
  694. In article <1992Nov16.014850.28678@cs.uoregon.edu> mkelly@mystix.cs.uoregon.edu (Michael A. Kelly) writes:
  695. >So, here are the resulting routines.  The first uses the jump table approach,
  696. >the second uses the wide mask approach.  Can they be made even faster??
  697.  
  698. Yes.
  699.  
  700. >    @2:                               ; copy the next eight bytes in the row
  701. >    
  702. >        MOVE.B    (A2), D2            ; copy the next mask byte
  703. >        
  704. >        TST.B     D2
  705.  
  706. A move instruction always does an implied tst, so you can just throw away
  707. the test instruction.
  708.  
  709. >        BEQ       @nocopy             ; if zero, don't copy anything
  710. >        
  711. >        CMPI.B    #0xFF, D2
  712. >        BNE       @hardway            ; don't copy everything
  713.  
  714. An addq.w #1, and then a beq might prove to be faster than the cmp with
  715. an immediate value. You have to adjust the mask back to its old value,
  716. if the test fails, but this can be done either with the jump tables
  717. (not with the ones you are using now, but the longer ones I will suggest
  718. later in this article) or by a subq.w #1
  719.  
  720. >        
  721. >        MOVE.L    (A0)+, (A1)+        ; copy all bytes
  722. >        MOVE.L    (A0)+, (A1)+
  723. >        ADDQ.L    #1, A2
  724.  
  725. Do a move.b (A2)+ instead of this instruction. I can't see any reason why
  726. you can't do the increment there.
  727.  
  728. >        JMP       @endloop
  729.  
  730. Copy the end of the loop here. So that you have the DBF instruction here
  731. instead of a JMP. Put the jump after the DBF. There's absolutely no reason
  732. to jump around when you can just use another DBF.
  733.  
  734. >    @nocopy:                          ; copy no bytes
  735. >        ADDQ.L    #8, A0
  736. >        ADDQ.L    #8, A1
  737. >        ADDQ.L    #1, A2
  738. >        JMP       @endloop
  739.  
  740. Same here as above.
  741.  
  742. >    @hardway:
  743. >        ANDI.L    #0xF0, D2           ; mask off the low four bits
  744. >        LSR.W     #4, D2              ; shift bits 4-7 into bits 0-3
  745.  
  746. The AND is totally wasted. The LSR will do the masking for you. This
  747. is assuming that you can keep the high bytes of D2 cleared. I think
  748. you should be able to do it. (I think it's already that way.)
  749.  
  750. You can also eliminate the and and lsr, if you use two 256-entry jump
  751. tables that simply ignore the high or low 4 bits. The tables will take
  752. some memory (2 x 4 x 256 bytes), but they are easy to construct with
  753. copy and paste.
  754.  
  755. >        ADD.W     D2, D2              ; double the index
  756. >        ADD.W     @table(D2.W), D2    ; calculate the address
  757. >        JSR       @table(D2.W)        ; plot four pixels
  758.  
  759. The 68020 has addressing modes that do the multiplication of the index.
  760. I haven't needed them myself, but I'm fairly certain that you can improve
  761. this part with the right addressing mode.
  762.  
  763. Replace the jsr with a LEA An to the return address and a JMP to the
  764. subroutine. Then jump back with a JMP (An). This is quite a bit faster
  765. than a JSR/RTS combination, although it's not "good style".
  766.  
  767. >        CLR.L     D2                  ; clear the mask register
  768. >        MOVE.B    (A2)+, D2           ; copy the next mask byte
  769. >        ANDI.B    #0xF, D2            ; mask off the high four bits
  770.  
  771. Use BFEXTU, if you must read the mask again. Remember that you can use
  772. - -1(A2), if you already incremented A2 or you might be able to account
  773. for this with the bitfield offset. You can also use constant bitfield
  774. offsets, if I remember correctly. I think you have some registers that
  775. you could use, so you could store fairly constant bitfield indices
  776. there.
  777.  
  778. >    @sub6:                            ; mask = 0110
  779. >        ADDQ.L    #1, A0
  780. >        ADDQ.L    #1, A1
  781. >        MOVE.B    (A0)+, (A1)+
  782.  
  783. This should be a move.w
  784.  
  785. >        ADDQ.L    #1, A0
  786. >        ADDQ.L    #1, A1
  787. >        RTS
  788. >    
  789. >    @sub8:                            ; mask = 1000
  790. >        MOVE.B    (A0)+, (A1)+
  791. >        ADDQ.L    #3, A0
  792. >        ADDQ.L    #3, A1
  793. >        RTS
  794.  
  795. A move.b (a0),(a1) along with addq #4 is faster on a 68000, but I
  796. don't think it matters on new processors. I may be wrong, but you'll
  797. probably never see the difference.
  798.  
  799. In the deep mask version, you could unroll the loop. It's kind of
  800. surprising the the 1 bit mask is actually faster, but it's mostly
  801. because of the superior algorithm that allows you to directly copy
  802. 8 bytes at a time in the most common case.
  803.  
  804. I think you did really well with the assembly. My changes will probably
  805. not make a big difference. I think 5% is the best you can hope for, but
  806. it might be as much as 10%. The only way to go beyond this is to make
  807. the move.l commands aligned on long word destinations, as I mentioned
  808. in my previous article.
  809.  
  810. I hope my articles offer proof for the other half of my .signature... :-)
  811. Can anyone do significantly better? I really love optimizing graphics
  812. routines.
  813.  
  814. - -- 
  815.   Juri Munkki                           Windsurf: fast sailing
  816.  jmunkki@hut.fi                          Macintosh: fast software
  817.  
  818. +++++++++++++++++++++++++++
  819.  
  820. From: mkelly@mystix.cs.uoregon.edu (Michael A. Kelly)
  821. Organization: High Risk Ventures
  822. Date: Wed, 18 Nov 1992 01:08:15 GMT
  823.  
  824. In article <1992Nov16.190947.9920@nntp.hut.fi> jmunkki@vipunen.hut.fi (Juri Munkki) writes:
  825. >In article <1992Nov16.014850.28678@cs.uoregon.edu> mkelly@mystix.cs.uoregon.edu (Michael A. Kelly) writes:
  826. >>        CMPI.B    #0xFF, D2
  827. >>        BNE       @hardway            ; don't copy everything
  828. >
  829. >An addq.w #1, and then a beq might prove to be faster than the cmp with
  830. >an immediate value. You have to adjust the mask back to its old value,
  831. >if the test fails, but this can be done either with the jump tables
  832. >(not with the ones you are using now, but the longer ones I will suggest
  833. >later in this article) or by a subq.w #1
  834.  
  835. According to the Motorola manual, you're right.  But in practice this slowed
  836. things down quite a bit.  I can't figure out why.  I replaced the CMPI with
  837. an ADDQ #1, then at @hardway I did a SUBQ #1.  My test case is a 32x32 rect
  838. with a 32x32 filled circle as the mask.  I think it would slow things down
  839. a lot more with more complicated masks.  But still, I don't know why it's
  840. slower, since the CMPI takes 8 clock cycles and the ADDQ and SUBQ each
  841. take 4, so it really should be faster....  Then again, those timings are
  842. for the 68000 (and 68020 too I think), and I'm using a 68040.
  843.  
  844. >>    @hardway:
  845. >>        ANDI.L    #0xF0, D2           ; mask off the low four bits
  846. >>        LSR.W     #4, D2              ; shift bits 4-7 into bits 0-3
  847. >
  848. >The AND is totally wasted. The LSR will do the masking for you.
  849.  
  850. :)  I don't know *what* I was thinking....
  851.  
  852. At this point, I ran my test again with the above modifications.  They
  853. improved the speed by about 10%.  (Changing the CMPI above decreased
  854. performance by about 30% with these other changes also in place.)
  855.  
  856. >You can also eliminate the and and lsr, if you use two 256-entry jump
  857. >tables that simply ignore the high or low 4 bits. The tables will take
  858. >some memory (2 x 4 x 256 bytes), but they are easy to construct with
  859. >copy and paste.
  860.  
  861. You mean two 16-entry jump tables, right?  I didn't implement this, but
  862. instead made a separate CopyMask function that used a single 256-entry
  863. jump table, with 256 subroutines for each of the 256 possible mask-bytes.
  864. See the code fragment below.
  865.  
  866. Hey, maybe I could just save 256 (times two) masks, AND each mask with the
  867. source and destination bytes, then OR those two results together to get
  868. the resulting pixel.  Hmmm, I wonder if it would be even faster....
  869.  
  870. >>        ADD.W     D2, D2              ; double the index
  871. >>        ADD.W     @table(D2.W), D2    ; calculate the address
  872. >>        JSR       @table(D2.W)        ; plot four pixels
  873. >
  874. >The 68020 has addressing modes that do the multiplication of the index.
  875. >I haven't needed them myself, but I'm fairly certain that you can improve
  876. >this part with the right addressing mode.
  877.  
  878. Nope, I don't think so.  You're talking about Address Register Indirect with
  879. Offset and Index, like so: @table( <no address in this case>, D2.W*2 ).
  880. The problem is that the value of D2 is preserved in that operation, so instead
  881. of D2 = (D2 * 2) + @table + (D2 * 2), you get D2 = D2 + @table + (D2 * 2).
  882.  
  883. >Replace the jsr with a LEA An to the return address and a JMP to the
  884. >subroutine. Then jump back with a JMP (An). This is quite a bit faster
  885. >than a JSR/RTS combination, although it's not "good style".
  886.  
  887. Wow, that made a big difference!  About a 17% improvement, making the total
  888. speedup about 25%.
  889.  
  890. >>        CLR.L     D2                  ; clear the mask register
  891. >>        MOVE.B    (A2)+, D2           ; copy the next mask byte
  892. >>        ANDI.B    #0xF, D2            ; mask off the high four bits
  893. >
  894. >Use BFEXTU, if you must read the mask again. Remember that you can use
  895. >-1(A2), if you already incremented A2 or you might be able to account
  896. >for this with the bitfield offset. You can also use constant bitfield
  897. >offsets, if I remember correctly. I think you have some registers that
  898. >you could use, so you could store fairly constant bitfield indices
  899. >there.
  900.  
  901. I'm not sure what you mean by constant offsets.  I did this:
  902.     BFEXTU    -1(A2){4:4}
  903. and it slowed it down by about 4%.
  904.  
  905. >>    @sub8:                            ; mask = 1000
  906. >>        MOVE.B    (A0)+, (A1)+
  907. >>        ADDQ.L    #3, A0
  908. >>        ADDQ.L    #3, A1
  909. >>        RTS
  910. >
  911. >A move.b (a0),(a1) along with addq #4 is faster on a 68000, but I
  912. >don't think it matters on new processors. I may be wrong, but you'll
  913. >probably never see the difference.
  914.  
  915. You're right, it didn't make any difference at all on my '040.
  916.  
  917. >In the deep mask version, you could unroll the loop. It's kind of
  918. >surprising the the 1 bit mask is actually faster, but it's mostly
  919. >because of the superior algorithm that allows you to directly copy
  920. >8 bytes at a time in the most common case.
  921.  
  922. I tossed that code.  I don't really think unrolling the loop will get it
  923. down to my current speed, which is more than twice as fast.
  924.  
  925. >it might be as much as 10%. The only way to go beyond this is to make
  926. >the move.l commands aligned on long word destinations, as I mentioned
  927. >in my previous article.
  928.  
  929. But as long as I align the source and destination Pixmaps, that isn't an
  930. issue, right?
  931.  
  932. >I hope my articles offer proof for the other half of my .signature... :-)
  933.  
  934. Definitely :)
  935.  
  936.  
  937. OK, here's the new code.  The first one is the newer, better version of
  938. Quick8CopyMask, with most of the optimizations suggested by Juri.  It's
  939. about 5.5 times as fast as QuickDraw's CopyMask, at least with my simple
  940. circle mask test case.  The second one is a small part of a very large
  941. Quick8CopyMask that has 256 separate subroutines to handle each mask
  942. byte, rather than only 16 subroutines to handle a mask nibble (a nibble is
  943. half a byte, right?).  It's far too long to post here, but if you want a
  944. copy I'll be happy to email it to you.  It's about 6.5 times as fast as
  945. CopyMask; about 15% faster than the short version.
  946.  
  947. I tested the routines with the mask used in the CalcCMask DTS snippet;
  948. the short version was 5.7 times as fast as CopyMask and the long version
  949. was 7 times as fast.
  950.  
  951. And once again, if anyone can improve on these routines, please tell me how!
  952.  
  953.  
  954. void Quick8CopyMask(
  955.     PixMapHandle    srcMap,
  956.     PixMapHandle    dstMap,
  957.     Ptr             mask,
  958.     Point           srcPt,
  959.     Point           dstPt,
  960.     short           width,
  961.     short           height )
  962. {
  963.  
  964.     register char   *src;
  965.     register char   *dst;
  966.     register long   srcNewline;
  967.     register long   dstNewline;
  968.     char            mode32 = QD32COMPATIBLE;
  969.     short           w = (width >> 3) - 1;
  970.     short           e = (width & 0x07) - 1;
  971.     short           h = height - 1;
  972.     
  973.     // Set up pointers to the beginning of the memory to copy
  974.     // and calculate the newline value for the source and destination
  975.     
  976.     src = GetPixBaseAddr( srcMap ) + (long) ((*srcMap)->rowBytes & 0x3fff) * srcPt.v + srcPt.h;
  977.     srcNewline = ((*srcMap)->rowBytes & 0x3fff) - width;
  978.     
  979.     dst = GetPixBaseAddr( dstMap ) + (long) ((*dstMap)->rowBytes & 0x3fff) * dstPt.v + dstPt.h;
  980.     dstNewline = ((*dstMap)->rowBytes & 0x3fff) - width;
  981.  
  982.     // Switch into 32 bit addressing mode
  983.     
  984.     SwapMMUMode( &mode32 );
  985.     
  986.     // Copy the rect from the source to the destination
  987.     
  988.     asm {
  989.     
  990.         MOVE.W     h, D0               ; put height loop variable in D0
  991.         MOVEA.L    src, A0             ; put the source pixmap address in A0
  992.         MOVEA.L    dst, A1             ; put the destination address in A1
  993.         MOVEA.L    mask, A2            ; put the mask address in A2
  994.         CLR.L      D2                  ; clear the mask register
  995.         
  996.     @1:                                ; copy the next row
  997.         MOVE.W     w, D1
  998.         
  999.     @2:                                ; copy the next eight bytes in the row
  1000.     
  1001.         MOVE.B     (A2)+, D2           ; copy the next mask byte
  1002.         BEQ        @nocopy             ; if zero, don't copy anything
  1003.         
  1004.         CMPI.B     #0xFF, D2
  1005.         BNE        @hardway            ; don't copy everything
  1006.         
  1007.         MOVE.L     (A0)+, (A1)+        ; copy all bytes
  1008.         MOVE.L     (A0)+, (A1)+
  1009.         
  1010.         DBF        D1, @2
  1011.         JMP        @endloop
  1012.     
  1013.     @nocopy:                           ; copy no bytes
  1014.         ADDQ.L     #8, A0
  1015.         ADDQ.L     #8, A1
  1016.         
  1017.         DBF        D1, @2
  1018.         JMP        @endloop
  1019.     
  1020.     @hardway:
  1021.         LSR.W      #4, D2              ; shift bits 4-7 into bits 0-3
  1022.         ADD.W      D2, D2              ; double the index
  1023.         ADD.W      @table(D2.W), D2    ; calculate the address
  1024.         LEA        @rts1, A3           ; save the return address
  1025.         JMP        @table(D2.W)        ; plot four pixels
  1026.     @rts1:
  1027.         
  1028.         MOVE.B     -1(A2), D2          ; copy the next mask byte
  1029.         ANDI.B     #0xF, D2            ; mask off the high four bits
  1030.         ADD.W      D2, D2              ; double the index
  1031.         ADD.W      @table(D2.W), D2    ; calculate the address
  1032.         LEA        @rts2, A3           ; save the return address
  1033.         JMP        @table(D2.W)        ; plot four pixels
  1034.     @rts2:
  1035.     
  1036.         DBF        D1, @2
  1037.         
  1038.     @endloop:
  1039.     
  1040.         TST.W      e
  1041.         BLT        @4                  ; continue if e is less than 0
  1042.     
  1043.         MOVE.B     (A2)+, D2           ; copy the next mask byte
  1044.         MOVE.W     e, D1               ; initialize the loop counter
  1045.         MOVEQ.L    #7, D3              ; initialize the bit counter
  1046.     
  1047.     @3:                                ; copy the next byte
  1048.         BTST       D3, D2              ; test the next bit in the mask
  1049.         BEQ        @skip               ; if zero, continue
  1050.         MOVE.B     (A0)+, (A1)+        ; else copy the pixel
  1051.         SUBQ.L     #1, D3              ; decrement the bit counter
  1052.         DBF        D1, @3
  1053.         JMP        @4
  1054.     @skip:
  1055.         ADDQ.L     #1, A0
  1056.         ADDQ.L     #1, A1
  1057.         SUBQ.L     #1, D3              ; decrement the bit counter
  1058.         DBF        D1, @3
  1059.     
  1060.     @4:
  1061.         ADDA.L     srcNewline, A0      ; bring the src pointer to the start of the next row
  1062.         ADDA.L     dstNewline, A1      ; bring the dst pointer to the start of the next row
  1063.         
  1064.         DBF        D0, @1
  1065.         
  1066.         JMP        @end                ; skip to the end
  1067.         
  1068.     @table:
  1069.         DC.W       @sub0
  1070.         DC.W       @sub1
  1071.         DC.W       @sub2
  1072.         DC.W       @sub3
  1073.         DC.W       @sub4
  1074.         DC.W       @sub5
  1075.         DC.W       @sub6
  1076.         DC.W       @sub7
  1077.         DC.W       @sub8
  1078.         DC.W       @sub9
  1079.         DC.W       @sub10
  1080.         DC.W       @sub11
  1081.         DC.W       @sub12
  1082.         DC.W       @sub13
  1083.         DC.W       @sub14
  1084.         DC.W       @sub15
  1085.     
  1086.     @sub0:                            ; mask = 0000, draw nothing
  1087.         ADDQ.L     #4, A0
  1088.         ADDQ.L     #4, A1
  1089.         JMP        (A3)               ; RTS
  1090.     
  1091.     @sub1:                            ; mask = 0001
  1092.         ADDQ.L     #3, A0
  1093.         ADDQ.L     #3, A1
  1094.         MOVE.B     (A0)+, (A1)+
  1095.         JMP        (A3)               ; RTS
  1096.     
  1097.     @sub2:                            ; mask = 0010
  1098.         ADDQ.L     #2, A0
  1099.         ADDQ.L     #2, A1
  1100.         MOVE.B     (A0), (A1)
  1101.         ADDQ.L     #2, A0
  1102.         ADDQ.L     #2, A1
  1103.         JMP        (A3)               ; RTS
  1104.     
  1105.     @sub3:                            ; mask = 0011
  1106.         ADDQ.L     #2, A0
  1107.         ADDQ.L     #2, A1
  1108.         MOVE.W     (A0)+, (A1)+
  1109.         JMP        (A3)               ; RTS
  1110.     
  1111.     @sub4:                            ; mask = 0100
  1112.         ADDQ.L     #1, A0
  1113.         ADDQ.L     #1, A1
  1114.         MOVE.B     (A0), (A1)
  1115.         ADDQ.L     #3, A0
  1116.         ADDQ.L     #3, A1
  1117.         JMP        (A3)               ; RTS
  1118.     
  1119.     @sub5:                            ; mask = 0101
  1120.         ADDQ.L     #1, A0
  1121.         ADDQ.L     #1, A1
  1122.         MOVE.B     (A0), (A1)
  1123.         ADDQ.L     #2, A0
  1124.         ADDQ.L     #2, A1
  1125.         MOVE.B     (A0)+, (A1)+
  1126.         JMP        (A3)               ; RTS
  1127.     
  1128.     @sub6:                            ; mask = 0110
  1129.         ADDQ.L     #1, A0
  1130.         ADDQ.L     #1, A1
  1131.         MOVE.W     (A0), (A1)
  1132.         ADDQ.L     #3, A0
  1133.         ADDQ.L     #3, A1
  1134.         JMP        (A3)               ; RTS
  1135.     
  1136.     @sub7:                            ; mask = 0111
  1137.         ADDQ.L     #1, A0
  1138.         ADDQ.L     #1, A1
  1139.         MOVE.B     (A0)+, (A1)+
  1140.         MOVE.W     (A0)+, (A1)+
  1141.         JMP        (A3)               ; RTS
  1142.     
  1143.     @sub8:                            ; mask = 1000
  1144.         MOVE.B     (A0), (A1)
  1145.         ADDQ.L     #4, A0
  1146.         ADDQ.L     #4, A1
  1147.         JMP        (A3)               ; RTS
  1148.     
  1149.     @sub9:                            ; mask = 1001
  1150.         MOVE.B     (A0), (A1)
  1151.         ADDQ.L     #3, A0
  1152.         ADDQ.L     #3, A1
  1153.         MOVE.B     (A0)+, (A1)+
  1154.         JMP        (A3)               ; RTS
  1155.     
  1156.     @sub10:                           ; mask = 1010
  1157.         MOVE.B     (A0), (A1)
  1158.         ADDQ.L     #2, A0
  1159.         ADDQ.L     #2, A1
  1160.         MOVE.B     (A0), (A1)
  1161.         ADDQ.L     #2, A0
  1162.         ADDQ.L     #2, A1
  1163.         JMP        (A3)               ; RTS
  1164.     
  1165.     @sub11:                           ; mask = 1011
  1166.         MOVE.B     (A0), (A1)
  1167.         ADDQ.L     #2, A0
  1168.         ADDQ.L     #2, A1
  1169.         MOVE.W     (A0)+, (A1)+
  1170.         JMP        (A3)               ; RTS
  1171.     
  1172.     @sub12:                           ; mask = 1100
  1173.         MOVE.W     (A0), (A1)
  1174.         ADDQ.L     #4, A0
  1175.         ADDQ.L     #4, A1
  1176.         JMP        (A3)               ; RTS
  1177.     
  1178.     @sub13:                           ; mask = 1101
  1179.         MOVE.W     (A0), (A1)
  1180.         ADDQ.L     #3, A0
  1181.         ADDQ.L     #3, A1
  1182.         MOVE.B     (A0)+, (A1)+
  1183.         JMP        (A3)               ; RTS
  1184.     
  1185.     @sub14:                           ; mask = 1110
  1186.         MOVE.W     (A0)+, (A1)+
  1187.         MOVE.B     (A0), (A1)
  1188.         ADDQ.L     #2, A0
  1189.         ADDQ.L     #2, A1
  1190.         JMP        (A3)               ; RTS
  1191.     
  1192.     @sub15:                           ; mask = 1111
  1193.         MOVE.L     (A0)+, (A1)+
  1194.         JMP        (A3)               ; RTS
  1195.     
  1196.     @end:
  1197.     
  1198.     }
  1199.     
  1200.     // Switch back to the previous addressing mode
  1201.     
  1202.     SwapMMUMode( &mode32 );
  1203.  
  1204. }
  1205.  
  1206.  
  1207.  
  1208.  
  1209. And this is the extremely long version, truncated for this posting:
  1210.  
  1211.  
  1212. void Quick8CopyMask(
  1213.     PixMapHandle    srcMap,
  1214.     PixMapHandle    dstMap,
  1215.     Ptr             mask,
  1216.     Point           srcPt,
  1217.     Point           dstPt,
  1218.     short           width,
  1219.     short           height )
  1220. {
  1221.  
  1222.     register char   *src;
  1223.     register char   *dst;
  1224.     register long   srcNewline;
  1225.     register long   dstNewline;
  1226.     char            mode32 = QD32COMPATIBLE;
  1227.     short           w = (width >> 3) - 1;
  1228.     short           e = (width & 0x07) - 1;
  1229.     short           h = height - 1;
  1230.     
  1231.     // Set up pointers to the beginning of the memory to copy
  1232.     // and calculate the newline value for the source and destination
  1233.     
  1234.     src = GetPixBaseAddr( srcMap ) + (long) ((*srcMap)->rowBytes & 0x3fff) * srcPt.v + srcPt.h;
  1235.     srcNewline = ((*srcMap)->rowBytes & 0x3fff) - width;
  1236.     
  1237.     dst = GetPixBaseAddr( dstMap ) + (long) ((*dstMap)->rowBytes & 0x3fff) * dstPt.v + dstPt.h;
  1238.     dstNewline = ((*dstMap)->rowBytes & 0x3fff) - width;
  1239.  
  1240.     // Switch into 32 bit addressing mode
  1241.     
  1242.     SwapMMUMode( &mode32 );
  1243.     
  1244.     // Copy the rect from the source to the destination
  1245.     
  1246.     asm {
  1247.     
  1248.         MOVE.W     h, D0               ; put height loop variable in D0
  1249.         MOVEA.L    src, A0             ; put the source pixmap address in A0
  1250.         MOVEA.L    dst, A1             ; put the destination address in A1
  1251.         MOVEA.L    mask, A2            ; put the mask address in A2
  1252.         CLR.L      D2                  ; clear the mask register
  1253.         
  1254.     @1:                                ; copy the next row
  1255.         MOVE.W     w, D1
  1256.         
  1257.     @2:                                ; copy the next eight bytes in the row
  1258.     
  1259.         CLR.W      D2                  ; clear the mask register
  1260.         MOVE.B     (A2)+, D2           ; copy the next mask byte
  1261.         BEQ        @nocopy             ; if zero, don't copy anything
  1262.         
  1263.         CMPI.B     #0xFF, D2
  1264.         BNE        @hardway            ; don't copy everything
  1265.         
  1266.         MOVE.L     (A0)+, (A1)+        ; copy all bytes
  1267.         MOVE.L     (A0)+, (A1)+
  1268.         
  1269.         DBF        D1, @2
  1270.         JMP        @endloop
  1271.     
  1272.     @nocopy:                           ; copy no bytes
  1273.         ADDQ.L     #8, A0
  1274.         ADDQ.L     #8, A1
  1275.         
  1276.         DBF        D1, @2
  1277.         JMP        @endloop
  1278.     
  1279.     @hardway:
  1280.         ADD.W      D2, D2              ; double the index
  1281.         ADD.W      @table(D2.W), D2    ; calculate the address
  1282.         JMP        @table(D2.W)        ; plot eight pixels
  1283.         
  1284.     @endloop:
  1285.     
  1286.         TST.W      e
  1287.         BLT        @4                  ; continue if e is less than 0
  1288.     
  1289.         MOVE.B     (A2)+, D2           ; copy the next mask byte
  1290.         MOVE.W     e, D1               ; initialize the loop counter
  1291.         MOVEQ.L    #7, D3              ; initialize the bit counter
  1292.     
  1293.     @3:                                ; copy the next byte
  1294.         BTST       D3, D2              ; test the next bit in the mask
  1295.         BEQ        @skip               ; if zero, continue
  1296.         MOVE.B     (A0)+, (A1)+        ; else copy the pixel
  1297.         SUBQ.L     #1, D3              ; decrement the bit counter
  1298.         DBF        D1, @3
  1299.         JMP        @4
  1300.     @skip:
  1301.         ADDQ.L     #1, A0
  1302.         ADDQ.L     #1, A1
  1303.         SUBQ.L     #1, D3              ; decrement the bit counter
  1304.         DBF        D1, @3
  1305.     
  1306.     @4:
  1307.         ADDA.L     srcNewline, A0      ; bring the src pointer to the start of the next row
  1308.         ADDA.L     dstNewline, A1      ; bring the dst pointer to the start of the next row
  1309.         
  1310.         DBF        D0, @1
  1311.         
  1312.         JMP        @end                ; skip to the end
  1313.         
  1314.     @table:
  1315.         DC.W       @sub0
  1316.         DC.W       @sub1
  1317.         DC.W       @sub2
  1318.         DC.W       @sub3
  1319.         .           .
  1320.         .           .
  1321.         .           .
  1322.         DC.W       @sub253
  1323.         DC.W       @sub254
  1324.         DC.W       @sub255
  1325.     
  1326.     @sub0:                            ; mask = 00000000
  1327.         ADDQ.L     #8, A0
  1328.         ADDQ.L     #8, A1
  1329.         DBF        D1, @2
  1330.         JMP        @endloop
  1331.     
  1332.     @sub1:                            ; mask = 00000001
  1333.         ADDQ.L     #7, A0
  1334.         ADDQ.L     #7, A1
  1335.         MOVE.B     (A0)+, (A1)+
  1336.         DBF        D1, @2
  1337.         JMP        @endloop
  1338.     
  1339.     @sub2:                            ; mask = 00000010
  1340.         ADDQ.L     #6, A0
  1341.         ADDQ.L     #6, A1
  1342.         MOVE.B     (A0), (A1)
  1343.         ADDQ.L     #2, A0
  1344.         ADDQ.L     #2, A1
  1345.         DBF        D1, @2
  1346.         JMP        @endloop
  1347.     
  1348.        .              .
  1349.        .              .
  1350.        .              .
  1351.     
  1352.     @sub182:                        ; mask = 10110110
  1353.         MOVE.B     (A0), (A1)
  1354.         ADDQ.L     #2, A0
  1355.         ADDQ.L     #2, A1
  1356.         MOVE.W     (A0), (A1)
  1357.         ADDQ.L     #3, A0
  1358.         ADDQ.L     #3, A1
  1359.         MOVE.W     (A0), (A1)
  1360.         ADDQ.L     #3, A0
  1361.         ADDQ.L     #3, A1
  1362.         DBF        D1, @2
  1363.         JMP        @endloop
  1364.     
  1365.     @sub183:                        ; mask = 10110111
  1366.         MOVE.B     (A0), (A1)
  1367.         ADDQ.L     #2, A0
  1368.         ADDQ.L     #2, A1
  1369.         MOVE.W     (A0), (A1)
  1370.         ADDQ.L     #3, A0
  1371.         ADDQ.L     #3, A1
  1372.         MOVE.B     (A0)+, (A1)+
  1373.         MOVE.W     (A0)+, (A1)+
  1374.         DBF        D1, @2
  1375.         JMP        @endloop
  1376.     
  1377.        .             .
  1378.        .             .
  1379.        .             .
  1380.     
  1381.     @sub253:                        ; mask = 11111101
  1382.         MOVE.L     (A0)+, (A1)+
  1383.         MOVE.W     (A0), (A1)
  1384.         ADDQ.L     #3, A0
  1385.         ADDQ.L     #3, A1
  1386.         MOVE.B     (A0)+, (A1)+
  1387.         DBF        D1, @2
  1388.         JMP        @endloop
  1389.     
  1390.     @sub254:                        ; mask = 11111110
  1391.         MOVE.L     (A0)+, (A1)+
  1392.         MOVE.W     (A0)+, (A1)+
  1393.         MOVE.B     (A0), (A1)
  1394.         ADDQ.L     #2, A0
  1395.         ADDQ.L     #2, A1
  1396.         DBF        D1, @2
  1397.         JMP        @endloop
  1398.     
  1399.     @sub255:                        ; mask = 11111111
  1400.         MOVE.L     (A0)+, (A1)+
  1401.         MOVE.L     (A0)+, (A1)+
  1402.         DBF        D1, @2
  1403.         JMP        @endloop
  1404.     
  1405.     @end:
  1406.     
  1407.     }
  1408.     
  1409.     // Switch back to the previous addressing mode
  1410.     
  1411.     SwapMMUMode( &mode32 );
  1412.  
  1413. }
  1414.  
  1415.  
  1416. - -- 
  1417. _____________________________________________________________________________
  1418. Michael A. Kelly                                               Senior Partner
  1419. mkelly@cs.uoregon.edu                                      High Risk Ventures
  1420. _____________________________________________________________________________
  1421.  
  1422. +++++++++++++++++++++++++++
  1423.  
  1424. From: jmunkki@vipunen.hut.fi (Juri Munkki)
  1425. Organization: Helsinki University of Technology
  1426. Date: Wed, 18 Nov 1992 20:33:45 GMT
  1427.  
  1428. In article <1992Nov18.010815.6649@cs.uoregon.edu> mkelly@mystix.cs.uoregon.edu (Michael A. Kelly) writes:
  1429. > According to the Motorola manual, you're right.  But in practice this slowed
  1430. > things down quite a bit.  I can't figure out why.  I replaced the CMPI with
  1431. > an ADDQ #1, then at @hardway I did a SUBQ #1.  My test case is a 32x32 rect
  1432. > with a 32x32 filled circle as the mask.  I think it would slow things down
  1433. > a lot more with more complicated masks.  But still, I don't know why it's
  1434. > slower, since the CMPI takes 8 clock cycles and the ADDQ and SUBQ each
  1435. > take 4, so it really should be faster....  Then again, those timings are
  1436. > for the 68000 (and 68020 too I think), and I'm using a 68040.
  1437.  
  1438. On the 040, the instructions can overlap quite a bit. I guess that the
  1439. modification of a data register prevented the overlap. I suggest that
  1440. you try storing the constant 0xFF in a free data register and doing
  1441. the compare with the data register. Register to register compares should
  1442. always be faster than immediate to register compares.
  1443.  
  1444. > >it might be as much as 10%. The only way to go beyond this is to make
  1445. > >the move.l commands aligned on long word destinations, as I mentioned
  1446. > >in my previous article.
  1447. > But as long as I align the source and destination Pixmaps, that isn't an
  1448. > issue, right?
  1449.  
  1450. I thought about this alignment stuff and it occured to me that the mask
  1451. bitmap would be a lot harder to use if you aligned your writes to video
  1452. RAM. On the Quadras, video RAM is so fast that alignment probably doesn't
  1453. matter all that much. On NuBUS, things are usually quite different.
  1454.  
  1455. > OK, here's the new code.  The first one is the newer, better version of
  1456. > Quick8CopyMask, with most of the optimizations suggested by Juri.  It's
  1457. > about 5.5 times as fast as QuickDraw's CopyMask, at least with my simple
  1458. > circle mask test case.  The second one is a small part of a very large
  1459. > Quick8CopyMask that has 256 separate subroutines to handle each mask
  1460. > byte, rather than only 16 subroutines to handle a mask nibble (a nibble is
  1461. > half a byte, right?).  It's far too long to post here, but if you want a
  1462. > copy I'll be happy to email it to you.  It's about 6.5 times as fast as
  1463. > CopyMask; about 15% faster than the short version.
  1464. > I tested the routines with the mask used in the CalcCMask DTS snippet;
  1465. > the short version was 5.7 times as fast as CopyMask and the long version
  1466. > was 7 times as fast.
  1467.  
  1468. It should be quite hard to improve speed from the longer code.  I bet it took
  1469. quite a few minutes to write it.  :-)
  1470.  
  1471. I do have an idea that you could try, if you still feel like the code should
  1472. be improved.
  1473.  
  1474. Snippet from long version:
  1475. >     @1:                                ; copy the next row
  1476. >         MOVE.W     w, D1
  1477. >     @2:                                ; copy the next eight bytes in the row
  1478. >         CLR.W      D2                  ; clear the mask register
  1479. >         MOVE.B     (A2)+, D2           ; copy the next mask byte
  1480. >         BEQ        @nocopy             ; if zero, don't copy anything
  1481. >         
  1482. >         CMPI.B     #0xFF, D2
  1483. >         BNE        @hardway            ; don't copy everything
  1484. >         
  1485. >         MOVE.L     (A0)+, (A1)+        ; copy all bytes
  1486. >         MOVE.L     (A0)+, (A1)+
  1487. >         
  1488. >         DBF        D1, @2
  1489. >         JMP        @endloop
  1490. >     
  1491. >     @nocopy:                           ; copy no bytes
  1492. >         ADDQ.L     #8, A0
  1493. >         ADDQ.L     #8, A1
  1494. >         
  1495. >         DBF        D1, @2
  1496. >         JMP        @endloop
  1497. >     
  1498. >     @hardway:
  1499. >         ADD.W      D2, D2              ; double the index
  1500. >         ADD.W      @table(D2.W), D2    ; calculate the address
  1501. >         JMP        @table(D2.W)        ; plot eight pixels
  1502.  
  1503. I finally dug up my 020 manual and went through the addressing modes.
  1504.  
  1505. Instead of having a jump table, you should probably use a table of jumps. :-)
  1506.  
  1507.         clr.w    D2
  1508. @1
  1509.         move.w    w,D1
  1510.  
  1511. @2
  1512.         move.b    (A2)+,D2
  1513.         jmp    (@jumptable,PC,D2.w*4)
  1514.  
  1515. @jumptable    bra.w    @mask0
  1516.         bra.w    @mask1
  1517.         bra.w    @mask2
  1518.         bra.w    @mask3
  1519.         ...
  1520.         bra.w    @mask254
  1521.         move.l    (A0)+,(A1)+    ; This is mask 255
  1522.         move.l    (A0)+,(A1)+
  1523.         dbf    D1,@2
  1524.         ...
  1525.  
  1526. I checked with Think C and at least the above code (or something similar)
  1527. to it compiles and the disassembly looks reasonable.
  1528.  
  1529. Note that i removed the special checks for 0 and 255. I think they are
  1530. mostly wasted, but it's possible they speed things with masks with large
  1531. solid areas.
  1532.  
  1533. - -- 
  1534.   Juri Munkki                           Windsurf: fast sailing
  1535.  jmunkki@hut.fi                          Macintosh: fast software
  1536.  
  1537. +++++++++++++++++++++++++++
  1538.  
  1539. From: mxmora@unix.SRI.COM (Matt Mora)
  1540. Date: 19 Nov 92 17:43:28 GMT
  1541. Organization: SRI International, Menlo Park, California
  1542.  
  1543. In article <1992Nov18.010815.6649@cs.uoregon.edu> mkelly@mystix.cs.uoregon.edu (Michael A. Kelly) writes:
  1544. >
  1545. >And once again, if anyone can improve on these routines, please tell me how!
  1546.  
  1547.  
  1548. If your going to be calling this function a lot (like in a tight loop
  1549. in a game to plot sprites) You can move the Swapmmumode code out of the
  1550. function and call before you make the call and restore it afterward.
  1551. That takes the two trap calls out of your fast code.
  1552.  
  1553. like:
  1554.  
  1555. swapmmumode(mode);
  1556.  
  1557. while plottingsprites
  1558.  QuickCopy(sprites[i++]);
  1559.  
  1560.  
  1561. swapmmumode(backtowhatiswas);
  1562.  
  1563. If you can find a way to precompute the addresses (like a table of row 
  1564. starting addresses) that might help. They mention stuff like this in one
  1565. of the develop articles.
  1566.  
  1567. Matt
  1568.  
  1569.  
  1570.  
  1571. - -- 
  1572. ___________________________________________________________
  1573. Matthew Mora                |   my Mac  Matt_Mora@sri.com
  1574. SRI International           |  my unix  mxmora@unix.sri.com
  1575. ___________________________________________________________
  1576.  
  1577. +++++++++++++++++++++++++++
  1578.  
  1579. From: mkelly@mystix.cs.uoregon.edu (Michael A. Kelly)
  1580. Organization: University of Oregon Computer and Information Sciences Dept.
  1581. Date: Fri, 20 Nov 1992 02:13:40 GMT
  1582.  
  1583.  
  1584. >On the 040, the instructions can overlap quite a bit. I guess that the
  1585. >modification of a data register prevented the overlap. I suggest that
  1586. >you try storing the constant 0xFF in a free data register and doing
  1587. >the compare with the data register. Register to register compares should
  1588. >always be faster than immediate to register compares.
  1589.  
  1590. I did that, and it doesn't seem to make any difference.  My timings are to
  1591. the 1/10000 of a tick.
  1592.  
  1593. >It should be quite hard to improve speed from the longer code.  I bet it took
  1594. >quite a few minutes to write it.  :-)
  1595.  
  1596. About 120, although I didn't do it all in one sitting so it's hard to say :^/
  1597.  
  1598. >I do have an idea that you could try, if you still feel like the code should
  1599. >be improved.
  1600. >Instead of having a jump table, you could probably use a table of jumps. :-)
  1601.  
  1602. It did make a very slight difference in the long version, but no difference
  1603. it the short version.  I think this is mostly because I still have to deal
  1604. with the mask in the short version, so it really didn't change much.
  1605.  
  1606. >Note that I removed the special checks for 0 and 255. I think they are
  1607. >mostly wasted, but it's possible they speed things with masks with large
  1608. >solid areas.
  1609.  
  1610. Removing them makes the long version faster, and the short version slower.
  1611.  
  1612. Here's the new long version, in its entirety.  Enjoy.
  1613.  
  1614. (This file must be converted with BinHex 4.0)
  1615. :$%0[F(P0BA0V,Q0`G!"338083e"$9!!!!!!@#!!!!!$YpJ%"!2%!!"A1J$CQGfG
  1616. BB&#!L!H!F'!)F!"JF!#!K`!!K3L!F!!!!!C`H!F)GQ"SGQG`L'!!F)#(!'D'CRD
  1617. )#)KhChKi!(J!K`L)J!!!!!#!#!"h!!#!B!!!!!!!#!#!!!!!!)#!!(!!!!!!!)!
  1618. !!(!!!!!)F)!!#!!!!!!!!)!)"`!!J!!!!!!(#!!"0$"8"38&!a)d3)LFJ69!U"0
  1619. 5P'K&L,NhDL3#G2&%d@Z5Iq1cTGMj9*GERM*hIAjVhRRLZhhYLhhqlAeBq(aTE[p
  1620. !f3k5TrAYql(%I[p%jaFm5h-kA8L6djVNaAr-e'ebGAPdBUeSc#BPZF&LYKa@12$
  1621. k-9#D*TEA**rm8&aljDd6YIjl9Yr+50bNZe*9p[4d4QA0$4kf'Qf!qk`"4'GVadV
  1622. 1jYhEfi$H[4M)M2LK1rNrK1hkNlQRk4XH*GLJ6QaXpGlfi[l2db[%#Xf1j%@GI`q
  1623. !'8U(q*(+[q2bjHR[B,ZrJ[e2*l+QRCZqVI`Ehf!!!!"#!!"iH+B!"J!!!!!!!!!
  1624. !!%!+!)ThLAPiBd9QC9D)#B#(H(LUUU#'UALALEQ3!!!!!!9fCAKPZ'GPB'9AKiQ
  1625. C#3!)N!!J!!4&9@ChGN0&4BHSHAL)L(QQCjH(LCZAHDKSX*KiH)-r46099RChGRH
  1626. *L)H'4@CQCfChLCQ)PjKiGkL*HBZVUCQUQCQULELCUkQ3!*!!QJLCTkLBQCQCHAU
  1627. E#VS!!!#kc!RdHI(,cbq(KEUqGAYZhrYTV[qC%5)JH,il,%dCr9FM-Zk[[YfrY+[
  1628. KC,[m0@`L)Ai@F*I5cKIEGZ[PGh5D@#iX0PH%YGflr"8'PUfflppZljFdYGZ'bcJ
  1629. NKGQ'VRP)V+ll[$KUXPAZebefAi@lUm#-8+`IVYqZQ[IHNcHckll0@&QZ@&dYPId
  1630. XN4K8VkqqbAED4NeQr$BSa$pC@lNNHh%4iV"q&QrKCICZ`[54GH`THR*4qN`pV"8
  1631. P!2cY,AqmV9EEf!@l5XEVeB%E3[$Pp[lNBJQcMJ4+UphbfN'9K'KbeAElE0DM&[4
  1632. Mar5kh@$dH6'Sd6rC!TYXaAm04%0jJ4EAIJSLf($&p[kiLKdU(A@lX-@)UDpH$5L
  1633. "%[54#rCG`+L(MEV`f0+)E,,IPX`Pbiriaib0mq9U89HVCA`aHFM$28ULT')Y5+l
  1634. EYhb+cAreRMYYh@+Sf4,fK'dQ"BXAIGVXSR,m*I$USRlIadpI4mIqG2[pa%KALBL
  1635. 1mL3VPkrA+MPPpmSD8IXD%ImdXReb8R5(B4)9#dQM12&k24,XX`PiEjEbYE*F-,%
  1636. 19pID3BlYaAbU@QZqc[Zi%LB*F0)pY*1bfTY9Hh9iEDm%lCfj-V5qPHh`XPhAF(G
  1637. 'U8!%E)3+rkX`*%GdehfG'[A`j**a55jCIG,N+h$PPbFKAF%4APqrem,[(TrE#bp
  1638. )c8GhGhFT,ib88A2p#*NQK@`TiA["+eTiX#qq56HNQUNHmX&8ii`#UD+YJ95MR5#
  1639. UD&&Bqd4GTK9hX#AK90p2MEJ6UF%I2G)PUNYlZXJDX[[+qBYVmSpl2'[ITdrRT)K
  1640. r*,c+4UmUk+dXLbqm9,ZiAGl8q&5,X(b9hpm[i+K1,6q2rhZjrdaE1DA9N!#b6HU
  1641. @r``A4TlEVYj)Z1&YID5r!6C!kXLLCp(2lb[-jTG$5Qc5cIIEpHmMQP#D91qKU6X
  1642. MBL*h"NlDYPeU6Q'K18YQ)LG6*FlD%j5fGfT-TN6,fqrmL-TkTNC*j286FYXV6Ym
  1643. T*DZ[GN5M(LpNHNL5rf)NUja@kcki5)reSE"iNE!8%%5kE@PbEFHe++j6FT+`&F`
  1644. ,TaFR42PqiM"NafC28JS92#*Irh6eiXA*l-#ZcjZ[fmh92RmI2Pj5*TrriGqpG%K
  1645. A[hm,Yj(XN[5hq(E`Zm#0l+Rj"$B6Er)rRdT43I(Xq*!!IP4VTpraaB[CP8DKGZ)
  1646. Q*,k&[G)L#hbfPEi5H(G)Mk(r$C4XpK@cTa,@lVF,DpT9U+Q58CHUl`,XSU*Km#2
  1647. Jr[54-U'Y-Lh69kBN+p$@lK3!G*!!dBZUK)9*-0)mK1dkQNa''+2@+0LTMl[J4Te
  1648. rcYhU-plTI[C`ZjPl@QTSQ3V416SMPqiV4V0YpM3"LA+V0T%Yl2ck8LM$38GGQVK
  1649. ChNUL38-A9drj)JT%YpP#k,AfC5'BS5)rV&dGA8ZBHPGkDN3,E*G@ANC2P8C)TI)
  1650. 8fIdVjj!!aGTFCUGmmNTUN!!GG(hIKA``A3#DEY5M$QME05[a-'(&h"R"KbmDC#$
  1651. 416T4Xa,MkC`+fYF`FL+Th28V["5ldrPd%YSq`PedVIfK$LX-D%1,X90#(&H@d)F
  1652. 4jl5KfCJKfGJKe,"$UQ#(D$-q!'Q"d'"m'#%'#-'#8'#F'#N'#X'#dc"DB0N-&TQ
  1653. #dc"DCJY-`@QB,6-&TQ#e"JY3B,8!rX`@S-&U$"DJ`@S-&U$"DJ`@bQ#f8`@bQ#f
  1654. 8)dM"E+B,C6"E+B,C6"E+B,CM"E-B,CM"E-B,CJZ8Q#fB`@c'#fB`@c'#fF`@cQ#
  1655. fF`@cQ#fF`@cKH!-&XjJYR-&XjJY5B,8Q#e*JY5B,8Q#e*JY5'`ZB,8Q#e*JY8B,
  1656. 9'#e4JY8B,9'#e4JY8B,9"Xh'#e4JYS-&Y"JYS-&Y"JYS-&Y"JYS-&Y"JYS$!+I!
  1657. - -'!I)'!M)'!R)'!V)'![)'!c)'!h)'!l)'!r)!J9PR!3(PT2,8H@XmYKjE6bh(P[
  1658. #A#!PaJ*FS#A1!PdJ*GB#AD!PhJ*H)#AQ!PkJ*Hi#Ab!PpJ*IS#Aq!8#!8'!8+!8
  1659. 1!85!8@!8D!8H!8L!8Q!8U!8Z!8b!8f!8k!8q!9#!9'!9+!91!95!9@!9D!9H!9L
  1660. !9Q!9U!9Z!9b!9f!9k!9q!@#!@'!@+!@1!@5!@@!@D!@H!@L!@Q!@U!@Z!@b!@f!
  1661. @k!@q!A#!A'!A+!A1!A5!A@!AD!AH!AL!AQ!AU!AZ!Ab!Af!Ak!Aq!J"-)!6#!%`
  1662. J"-)!6#!%`J"-)!6#!%`J"-)!6#!%`J"-)!6#!%`J"-)!6#!%`J"-)!62Q`2Qa9c
  1663. C!)$jY$jY6jYMjYcjZ$jZ3J"-)!6#!%`J"-)!6#!%`J"-)!6#!%`J"-)!6#!%`J"
  1664. - -)!6#!%`J"-)!6#!%`J"-)!6#!%`J"-)!6#!%`J"-X!#q!dU&jLpdj,a&qmhL,qh
  1665. pFIU6mB[i5JRj0[m5GKhr5XhArH-AaTNmX+E+-KH9lbVf)C%f),[$Y8(L'+L"5Ql
  1666. f0+-C5r4eJaRbP6TF`SR-!`Bd9%&hRa$PmIKQ#AQIB-`idYJjFb(1$-5SJ$-8fB0
  1667. +56"0#SJi(p,bl!$fTS$`"rk29S'99Q2N6iEec)X`-b+L$Fd%Rk-X%d+L$Jd(-l!
  1668. Je0#CK"XHLpl@fGB6-,@l`%bEk"EX`#&,X#$NhCTD%picUP34a%M1DQFj3"*83ER
  1669. rYLD-3MSD`Le0DCK&XI8pid)4J3)+'%D6-8-ER!l2TL-MFp+4KGFm5-cYc3LAAL*
  1670. HrFAA1fq!q,Fm)Pi+%R&[6Pe%R5h3KEb#Kk@k&Ah$E+IB8)J2a%akF6c&$rfkSZC
  1671. SG95G5JV1(lHJUdCXh1Q3!%MUZ8*'f[QB5Bha(*[4eh5&$#0CQ+'DD1XHKi@*NBA
  1672. A2&0'Hep-m,#LJfem,HR'&Miaj62#a-M#kiD-KhhPjF20!NA"ZS0p*![L-4McU*N
  1673. BAa16VR5l-HHSYmXf3YMHD'8i`521SQ%R&b$PLfG,Ibe"!C!!aQ+"Y@1FTdeU2!b
  1674. '#4KP1A#KDM`-KMpl``j)Rf&#e$`C)K&!j1(Mq+ke'K1ZfiZQ6hd"f$M"*DJ!RHf
  1675. I6MfH6HK@DdZ6CU",1P4XjU'3!%9BF*C4fU1TQ%Rlm36)8+`L5bZR$$Q+"lN6qHI
  1676. &@CC)M#kjbr`-b»@H-AA$`T2je9+&CPNL-,VKTF(*X[,LXj5A(q,iKUQImq*
  1677. @HK)M#q*dSFF[BFV23Pb0Lm3GJ*r,,-UY$L4'&iJ[i(%8[,Le0iF$`P`@S$#Th(T
  1678. E#`'9)3N*'GC1&Uc!i4K&$MhXR@6KDX`1'35-2IiGC1&V!!pI3PaM'8-M6l&aDaB
  1679. 2fZ%Z)64RRVbSVL2TLCfV(aRXbS`h(da-`N1HaEkV)`X(da$X@$RXIJM)`X(da#4
  1680. Kcf,K3LU3!2TL(D8(2Br"'46JI6%*'(2Bkh8%8i(da#A"cf2`4N8i(da#4Kcf2%I
  1681. )T52TL'KiFDbi8)SH2Ti%8$LiH)q@SdIV0J1)393H4F@TqIV-N!#A"@E1LT3[1Q,
  1682. fKi9RB+%8l&&!ZQ(UH24db+M#HTiA5MDjHMZd9"rEqZ0p$aR`FjcdAPi0fS%%6jJ
  1683. ##SJ24'm!JHIP,TcDVD3K&J%A52`mm5IY*1'(-8-Fc`fa6%Bp!#C'&eca)cf`)(R
  1684. U6q"iZZHh&)q1ZTR21SQ4KGF1LSH$CFA+6cj*kUaI%blB![L-4Md)*NBAa-Ra$`E
  1685. *Mip##Id(&iMflDcia+8RSC6)`[%(Z4$LbAPa&0TqL)5i2Fi'&6Z25-+jqL)6d*k
  1686. GC1%9Q(k)K,MMq'6V*`LX`r4%*'(`m1XR#eJ!G%3PaX,Jjb@4F@X@$SL%Z),M226
  1687. 8LZ)r4%c&$2D)E1a!b)b+iMqaB5-2e'I&fDT&J"qNS5i2eF'3!%Gak4CSITq%p#H
  1688. Ra-p8Lc3rYb%Z$B!2D`Gak4CSIh6#HK23XSIbd8M&3IhY#e!5i2EBGak4MF2m$K2
  1689. 3RSH3!-@15-H4rL%*b(rr*q#-M'iIi`#4KrXTmAMY#fkJ2)B-M3dh1-5fKEUL$b'
  1690. #HKTZDLiYe4"j$(cXE$b4',Lfl-2*%*F(NiIkFPYh`[dK%j2!j*pT`F!aL-YZN!"
  1691. 2JS*&HVpfI*[S9aDTDNAQ2AEDUT!!63U)20abYKLI[e%*$VL@&#YB5I6H)S(A%rL
  1692. f+eK*rZQ*'(A%J8%#a46aQ&,mbmZPQipQN!!MT91+NFAB$VN(M!D#kP8kFZMG6(A
  1693. +cI)P83U4aI)1ZCqAM*8UQ(lpIBf138GdeS)k9+bT(&i`kk"am0"G5Tf1A4Ze$RT
  1694. (Z5Z-6#@'"8pM%`*l(A8d&e+Y8#k-d`kkhY*iP@XU4aI)1Z`C2&jGKD`TqYGSrjV
  1695. (ADh&eDb"qYGc-ZXmqm3LZakklh$lH@cKpEa5LXjkkdR!8XH(jcG3LXjkr28I"k0
  1696. bjY,U+j(Vmf",VSYd)V'HZVXIap(LUE5kM$3pG6`PdFI,R%JM$mpG3)R-H0CY,U+
  1697. e(VmT",SmGLT9QV@BAcI[5m%lI-Dq1Eq*Ui3NJQiZSGS6(b!1`8Ui3LLPkA,R,dN
  1698. M!4Y)kZ'")MLl!I)%eLPA#86dd#YVRDZEa3V8U88$X81PUecG-V8S4$R+iS1e+hL
  1699. G@PiRkM$Z&MdU03H#JN[#d821qq*8!b+L$Ldi*R#X`iflBeE0b9dYH8"!j,i&*ED
  1700. ,["$(FD(Da!6"#&-60c-RJ*RYY)8"!"2j6TE5,[",0B(@5UJ&J8%lFc-Caal#5mH
  1701. JZLBpIHfQaQp&dJ#F&c8%iZN(HQD3!2JULImd[[EUCrFFkrRY3#U+#Gr@CMqb6Qk
  1702. 49Fd[S`,T"pH3!1$&F@eCRNLf,f"pD6cHaP9R85rP0m[B(eC0BR9R95%ljG)2fZ2
  1703. bk4@JT,qX"G)22NiZk9SJ)28re*HipHfrVP9RX**-rd%XqAYXKbhLG@Ha+"Ji-Kr
  1704. [*aG)LJTGcR#241)h##PY&(blR-%6MUVEakLMjGcP#24Sq(HF+@d8I,ZFJ5f*E(,
  1705. Sqaac)TbAFi`R&RAH-4J&Y&8blR%%YLpKdNkc)Um5rl!1`'-0N!!R49-Pp+a%i`f
  1706. HBp@`Y-FcJqq)Bkh!YV9R-FcJPXBE,akY@Fac12Ij1MQb!HVBF'1E)4k1FKj&0DX
  1707. B[Z0G5QjDH@m6VB8LHF-AFMk[&iHcUdrTHL%BAMm*8#C@NT2bD#*amXcc(UY'LIK
  1708. 6%HMiK4P38,DY0#IH0%YMh[hQ29DD%q-84k00!lr5KE9TS6hr",BPXFic`1+Y6LG
  1709. MBJNF4Sa'+&Y@VT1V`5f-0Ae*dV9dRXFL8aD129S8,DZ'j0q!LF5f2@'GjaT@X"B
  1710. rCF*a&XSd*1pTkf+TMa4#*a,EVI-T@a[-H0B4kirN!khc+9XEc(Md%YMrTYmhc&Y
  1711. e*Mm@K1,'f50kjY1+fl3aq4`R&#D2E4h5M(Sbe"-ikhA4j5M'!be"-a11Maf$e'+
  1712. TPU#(CU(4ii&Y'+TPU#%YMSmA$fT&Qc,8%1#FG(MJ@d@"-Y33PXG(MV**8L`*PU#
  1713. %iMSmF#fL`*PU#%YMSmH464A)be"$K`(VT,akM$Jbe-#24kfMb+DeB`k!"k`!cF2
  1714. ZF9V%"l83R%CcL&0&NaIF5-3Jr0TaHJL`JSp&k!r0Tp5+D%@2%pkJ[2Ep`)EZVD8
  1715. N@+N3m5L1X4S$Tk`5m3km1Zf,DS!k9%(&TakPd[idEUBU3CN9%"MS25BRr#bq1Pb
  1716. d($DpE41HMC-6[lbDZQh4S22BRr`PZEJaj&akq`#Fmd5BR(lIKbR1,T!!maLImT2
  1717. PdJm$6@*c`f6%iZN(Xc,`r2)H!5Ip0YcKQIhL2LVbH3kVTLFHr@08cLk4#lX1RS4
  1718. k154e`C6b)QKBG)`PXFKcF286V-2BNkFXSh2!%k*eQ(3m)R'jih$eDJTUrHSIc*(
  1719. X'Y`p@SJDY1c-HXprQH4%pM$5%2NhC3jEa1LHaKT#%6ZA8#FR53LJTKT#%HLFAcF
  1720. )K&(c$5%*EF[2bj[B44m`dK#24SqGGjbF)SqBD3K,BPXFZM@F88j-0)3R%I8fqSM
  1721. %96-0)3PYdH`q*1N9H-0)3qh`BE0!R49-`rD`41-0QiHVB@Q1C`G8!aeZ"E@V1Bj
  1722. R",B`fA$fDeCc(-ipriG(0N!p@`i-Ff3MdFj$b+DeBaIhqG35RP,(C-!6VB8LHR)
  1723. @hMk`'J2+drTFk$fl6$Ck0@m%+d@*qQPl!Pqr*8hKp@IK2d-$riBI"@makV3FRjX
  1724. 4(V,ZMYiI9R`6rf82rdSrC#makV1NRcAL24U2I0bbK@HK2a[#8aUlHBp9RS6mE`M
  1725. ebRqFhC+cP*q'BHCjkGYGeXDS9RJ6r!X4k2F[I9d*DK9'8)5Q,4a@)$LY6mM+`*a
  1726. (ZYZF3Le@5-Y`P-FYcD29UXNCEK(SlrlQp"E$-MSf%TL`TcZl,BI%H``35kF,H8q
  1727. %J['kdKpfm9P3!NU)20U@8p2a@CY+Q1eQG)-c+L$kMZbRT55",4(Fl35fF33aK0S
  1728. R24iQ*fjZ$`%lfflXTjmLJPTflR,lQ"i,Qd6RQS6%lqphmCaR0dKjNNKkqpY4i$d
  1729. H!*V%jiH*LFA5$fE'N!$ji%%lpc6fDeGZSFQlD#$bqT!!)EGc)FJ6NfE-LAcb+lA
  1730. ic0$YUYSm4!8JmIrFd1q`l!mL$A'h4rM[40B(3[A'h4r$el(jE)L"hPEfaM%Xm!i
  1731. 81N3hdP%hmP3AdHLACifiDYNZf[9mjBA5`f@5hm,2TEGiAbVekq&PppZlj5llYGK
  1732. 8bl2'[ITdrRT)Kr*,c&4DLFZAeBmImJ!!!!)G1+d!!3!&FA9TBfX"!!!!#&4&@&4
  1733. ,38K-Tc&rQUFaIj)"!,pi'U3!"J!!!C)!!0M9!!!"GJ!!&&!R5J!!:
  1734.  
  1735.  
  1736. - -- 
  1737. _____________________________________________________________________________
  1738. Michael A. Kelly                                               Senior Partner
  1739. mkelly@cs.uoregon.edu                                      High Risk Ventures
  1740. _____________________________________________________________________________
  1741.  
  1742. +++++++++++++++++++++++++++
  1743.  
  1744. From: Steve Christensen <stevec@apple.com>
  1745. Date: Fri, 20 Nov 1992 04:06:08 GMT
  1746. Organization: Apple Computer, Inc.
  1747.  
  1748. I took a pass at the code and here's what I came up with (changed lines
  1749. will have a * in the comment field).
  1750.  
  1751.         MOVE.W     h, D0               ; put height loop variable in D0
  1752.         MOVEA.L    src, A0             ; put the source pixmap address in
  1753. A0
  1754.         MOVEA.L    dst, A1             ; put the destination address in A1
  1755.         MOVEA.L    mask, A2            ; put the mask address in A2
  1756.         CLR.L      D2                  ; clear the mask register
  1757.     @1:                                ; copy the next row
  1758.         MOVE.W     w, D1
  1759.     @2:                                ; copy the next eight bytes in the
  1760. row
  1761.         MOVE.B     (A2)+, D2           ; copy the next mask byte
  1762.         BEQ.S      @nocopy             ;*if zero, don't copy anything
  1763.         
  1764.         CMPI.B     #0xFF, D2
  1765.         BNE.S      @hardway            ;*don't copy everything
  1766.         
  1767.         MOVE.L     (A0)+, (A1)+        ; copy all bytes
  1768.         MOVE.L     (A0)+, (A1)+
  1769.         DBF        D1, @2
  1770.         BRA.S      @endloop            ;*
  1771.     
  1772.     @nocopy:                           ; copy no bytes
  1773.         ADDQ.L     #8, A0
  1774.         ADDQ.L     #8, A1
  1775.         DBF        D1, @2
  1776.         BRA.S      @endloop            ;*
  1777.     
  1778.     @hardway:
  1779.         MOVEQ      #0xF, D3            ;*mask off the lower nibble for
  1780. later
  1781.         AND.B      D2, D3              ;*
  1782.         LSR.W      #4, D2              ; shift bits 4-7 into bits 0-3
  1783.         ADD.W      D2, D2              ; double the index
  1784.         MOVE.W     @table(D2.W), D2    ;*calculate the address
  1785.         LEA        @rts1, A3           ; save the return address
  1786.         JMP        @table(D2.W)        ; plot four pixels
  1787.     @rts1:
  1788.         
  1789. ;*******MOVE.B     -1(A2), D2          ; copy the next mask byte
  1790. ;*******ANDI.B     #0xF, D2            ; mask off the high four bits
  1791.         ADD.W      D2, D2              ; double the index
  1792.         MOVE.W     @table(D3.W), D2    ;*calculate the address
  1793.         LEA        @rts2, A3           ; save the return address
  1794.         JMP        @table(D2.W)        ; plot four pixels
  1795.     @rts2:
  1796.         DBF        D1, @2
  1797.         
  1798.     @endloop:
  1799.     
  1800.         MOVE.W     e, D1               ;*
  1801.         BLT.S      @4                  ;*continue if e is less than 0
  1802.     
  1803.         MOVE.B     (A2)+, D2           ; copy the next mask byte
  1804. ;*******MOVE.W     e, D1               ; initialize the loop counter
  1805.         MOVEQ.L    #7, D3              ; initialize the bit counter
  1806.     
  1807.     @3:                                ; copy the next byte
  1808.         BTST       D3, D2              ; test the next bit in the mask
  1809.         BEQ.S      @skip               ;*if zero, continue
  1810.         MOVE.B     (A0)+, (A1)+        ; else copy the pixel
  1811.         SUBQ.L     #1, D3              ; decrement the bit counter
  1812.         DBF        D1, @3
  1813.         BRA.S      @4                  ;*
  1814.     @skip:
  1815.         ADDQ.L     #1, A0
  1816.         ADDQ.L     #1, A1
  1817.         SUBQ.L     #1, D3              ; decrement the bit counter
  1818.         DBF        D1, @3
  1819.     
  1820.     @4:
  1821.         ADDA.L     srcNewline, A0      ; bring the src pointer to the
  1822. start of the next row
  1823.         ADDA.L     dstNewline, A1      ; bring the dst pointer to the
  1824. start of the next row
  1825.         
  1826.         DBF        D0, @1
  1827.         
  1828.         JMP        @end                ; skip to the end
  1829.         
  1830.     @table:
  1831.         DC.W       @sub0-@table        ;*
  1832.         DC.W       @sub1-@table        ;*
  1833.         DC.W       @sub2-@table        ;*
  1834.         DC.W       @sub3-@table        ;*
  1835.         DC.W       @sub4-@table        ;*
  1836.         DC.W       @sub5-@table        ;*
  1837.         DC.W       @sub6-@table        ;*
  1838.         DC.W       @sub7-@table        ;*
  1839.         DC.W       @sub8-@table        ;*
  1840.         DC.W       @sub9-@table        ;*
  1841.         DC.W       @sub10-@table       ;*
  1842.         DC.W       @sub11-@table       ;*
  1843.         DC.W       @sub12-@table       ;*
  1844.         DC.W       @sub13-@table       ;*
  1845.         DC.W       @sub14-@table       ;*
  1846.         DC.W       @sub15-@table       ;*
  1847.     
  1848.     @sub0:                            ; mask = 0000, draw nothing
  1849.         ADDQ.L     #4, A0
  1850.         ADDQ.L     #4, A1
  1851.         JMP        (A3)               ; RTS
  1852.     
  1853.     @sub1:                            ; mask = 0001
  1854.         ADDQ.L     #3, A0
  1855.         ADDQ.L     #3, A1
  1856.         MOVE.B     (A0)+, (A1)+
  1857.         JMP        (A3)               ; RTS
  1858.     
  1859.     @sub2:                            ; mask = 0010
  1860.         ADDQ.L     #2, A0
  1861.         ADDQ.L     #2, A1
  1862.         MOVE.B     (A0), (A1)
  1863.         ADDQ.L     #2, A0
  1864.         ADDQ.L     #2, A1
  1865.         JMP        (A3)               ; RTS
  1866.     
  1867.     @sub3:                            ; mask = 0011
  1868.         ADDQ.L     #2, A0
  1869.         ADDQ.L     #2, A1
  1870.         MOVE.W     (A0)+, (A1)+
  1871.         JMP        (A3)               ; RTS
  1872.     
  1873.     @sub4:                            ; mask = 0100
  1874.         ADDQ.L     #1, A0
  1875.         ADDQ.L     #1, A1
  1876.         MOVE.B     (A0), (A1)
  1877.         ADDQ.L     #3, A0
  1878.         ADDQ.L     #3, A1
  1879.         JMP        (A3)               ; RTS
  1880.     
  1881.     @sub5:                            ; mask = 0101
  1882.         ADDQ.L     #1, A0
  1883.         ADDQ.L     #1, A1
  1884.         MOVE.B     (A0), (A1)
  1885.         ADDQ.L     #2, A0
  1886.         ADDQ.L     #2, A1
  1887.         MOVE.B     (A0)+, (A1)+
  1888.         JMP        (A3)               ; RTS
  1889.     
  1890.     @sub6:                            ; mask = 0110
  1891.         ADDQ.L     #1, A0
  1892.         ADDQ.L     #1, A1
  1893.         MOVE.W     (A0), (A1)
  1894.         ADDQ.L     #3, A0
  1895.         ADDQ.L     #3, A1
  1896.         JMP        (A3)               ; RTS
  1897.     
  1898.     @sub7:                            ; mask = 0111
  1899.         ADDQ.L     #1, A0
  1900.         ADDQ.L     #1, A1
  1901.         MOVE.B     (A0)+, (A1)+
  1902.         MOVE.W     (A0)+, (A1)+
  1903.         JMP        (A3)               ; RTS
  1904.     
  1905.     @sub8:                            ; mask = 1000
  1906.         MOVE.B     (A0), (A1)
  1907.         ADDQ.L     #4, A0
  1908.         ADDQ.L     #4, A1
  1909.         JMP        (A3)               ; RTS
  1910.     
  1911.     @sub9:                            ; mask = 1001
  1912.         MOVE.B     (A0), (A1)
  1913.         ADDQ.L     #3, A0
  1914.         ADDQ.L     #3, A1
  1915.         MOVE.B     (A0)+, (A1)+
  1916.         JMP        (A3)               ; RTS
  1917.     
  1918.     @sub10:                           ; mask = 1010
  1919.         MOVE.B     (A0), (A1)
  1920.         ADDQ.L     #2, A0
  1921.         ADDQ.L     #2, A1
  1922.         MOVE.B     (A0), (A1)
  1923.         ADDQ.L     #2, A0
  1924.         ADDQ.L     #2, A1
  1925.         JMP        (A3)               ; RTS
  1926.     
  1927.     @sub11:                           ; mask = 1011
  1928.         MOVE.B     (A0), (A1)
  1929.         ADDQ.L     #2, A0
  1930.         ADDQ.L     #2, A1
  1931.         MOVE.W     (A0)+, (A1)+
  1932.         JMP        (A3)               ; RTS
  1933.     
  1934.     @sub12:                           ; mask = 1100
  1935.         MOVE.W     (A0), (A1)
  1936.         ADDQ.L     #4, A0
  1937.         ADDQ.L     #4, A1
  1938.         JMP        (A3)               ; RTS
  1939.     
  1940.     @sub13:                           ; mask = 1101
  1941.         MOVE.W     (A0), (A1)
  1942.         ADDQ.L     #3, A0
  1943.         ADDQ.L     #3, A1
  1944.         MOVE.B     (A0)+, (A1)+
  1945.         JMP        (A3)               ; RTS
  1946.     
  1947.     @sub14:                           ; mask = 1110
  1948.         MOVE.W     (A0)+, (A1)+
  1949.         MOVE.B     (A0), (A1)
  1950.         ADDQ.L     #2, A0
  1951.         ADDQ.L     #2, A1
  1952.         JMP        (A3)               ; RTS
  1953.     
  1954.     @sub15:                           ; mask = 1111
  1955.         MOVE.L     (A0)+, (A1)+
  1956.         JMP        (A3)               ; RTS
  1957.     
  1958.     @end:
  1959.  
  1960. ---------------------------
  1961.  
  1962. End of C.S.M.P. Digest
  1963. **********************
  1964.